package com.dimpt.cloud.config;

import com.alibaba.fastjson.JSON;
import com.dimpt.cloud.userauth.model.TokenSO;
import com.dimpt.cloud.userauth.service.IAuthService;
import com.dimpt.cloud.utils.ParseHttp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.*;

/***************
 * token验证拦截
 * @author liujf
 * @time 2017-08-01
 */
//@Component
//@WebFilter(urlPatterns = { "/*" }, filterName = "TokenAuthFilter")
public class TokenAuthFilter implements Filter {

    private final static List<String> ERROR_INFO = Arrays.asList(
            "用户授权认证失败，客户端请求参数中无token信息",
            "用户授权认证失败，客户端请求参数token信息无效",
            "用户授权认证失败，客户端请求无权限访问"
    );

    private final static int AUTH_SUCCESS = -1;
    private static Logger logger = LoggerFactory.getLogger(TokenAuthFilter.class);

    @Autowired
    IAuthService authService;

    // 读取path配置，by lifq 20180622
    @Value("${server.context-path}")
    private String path;

    @Override
    public void destroy() {

    }

    /**
     * 鉴权过滤器
     *
     * @param request
     * @param response
     * @param chain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {

        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse rep = (HttpServletResponse) response;

        //提取鉴权头信息
        String token = ParseHttp.getHttpAuthorization(req);
        String method = req.getMethod();
        String uri = req.getRequestURI();
        int ret = auth(token,method,uri);

        if (ret == AUTH_SUCCESS) {
            chain.doFilter(request, response);
            return;
        }
        //返回未授权的错误信息
        this.authError(rep,ERROR_INFO.get(ret));
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {

    }

    /**
     * 对HTTP请求进行鉴权，返回错误信息
     *
     * @param token
     * @param method
     * @param uri
     * @return
     */
    private int auth(String token,String method,String uri) {

        //String path = env.getProperty("server.context-path");
        uri = uri.substring(path.length());
        //首先判断uri是否可直接访问
        if(authPermit(method,uri)){
            return AUTH_SUCCESS;  //鉴权成功返回-1
        }else{
            if(token == null){  //客户端请求无token信息
                return 0;
            }else{
                TokenSO tokenObj;
                // 然后判断token是否有效
                if(((tokenObj = authToken(token)) == null)){    //无效
                    return 1;
                }else{  // 到此为止，以上判断token有效，接下来判断用户角色是否有请求本url的权限
                    //如果uri不可直接访问，并且token有效，则判断用户是否有权访问uri
                    if (authService.auth(tokenObj.getRole(),method,uri)) {
                        return AUTH_SUCCESS;  //鉴权成功返回-1
                    }else{  //uri不可被访问
                        return 2;
                    }
                }
            }
        }
    }

    /**
     * 对HTTP头中的Authentication:Bear XXXX 进行鉴权，判断XXXX是否是数据库中存在token，
     * 如果是，则通过，并返回用户信息；如果不是，则鉴权失败
     *
     * @param token
     * @return
     */
    private TokenSO authToken(String token) {
        //提取鉴权头信息
        return authService.validateToken(token);
    }

    /**
     * 判断uri是否是可直接访问的资源
     *
     * @param httpMethod
     * @param uri
     * @return
     */
    private boolean authPermit(String httpMethod,String uri) {
    //    logger.info("auth permit: "+httpMethod+"|"+uri);
        return authService.isPermit(httpMethod,uri);
    }

    /**
     *
     * @param rep
     */
    private void authError(HttpServletResponse rep,String errMsg) throws IOException {
        PrintWriter writer = null;
        OutputStreamWriter osw = null;
        try {
            rep.setCharacterEncoding("UTF-8");
            rep.setContentType("application/json; charset=utf-8");
            rep.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            osw = new OutputStreamWriter(rep.getOutputStream(),"UTF-8");
            writer = new PrintWriter(osw, true);
            Map map = new HashMap();
            map.put("code",HttpServletResponse.SC_UNAUTHORIZED);
            map.put("message",errMsg);
            map.put("data",new ArrayList<>());
            writer.write(JSON.toJSONString(map));
            writer.flush();
            writer.close();
            osw.close();
        } catch (UnsupportedEncodingException e) {
            logger.error("过滤器返回信息失败:" + e.getMessage(), e);
        } catch (IOException e) {
            logger.error("过滤器返回信息失败:" + e.getMessage(), e);
        } finally {
            if (null != writer) {
                writer.close();
            }
            if (null != osw) {
                osw.close();
            }
        }
    }
}